﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Threading;
using QuailDashboardClient.QuailTestRunnerServiceReference;
using BinaryCompressedPackageFile=Quail.BinaryCompressedPackageFile.BinaryCompressedPackageFile;

namespace QuailDashboardClient
{
    class Program
    {
        static int Main(string[] rawargs)
        {
            var args = new Arguments(rawargs);

            if (args.Count == 0)
            {
                ShowCommandLine();
                return 1;
            }

            // Array.ForEach(rawargs, Console.WriteLine);

            try
            {
                var didsomething = false;
                var returncode = 0;
                if (args.Exists("u"))
                {
                    UploadFiles(args);
                    didsomething = true;
                }
                if (args.Exists("r"))
                {
                    returncode = RunTests(args);
                    didsomething = true;
                }

                if (!didsomething)
                {
                    ShowCommandLine();
                    return 1;
                }

                return returncode;
            }
            catch (Exception ex)
            {
                Console.WriteLine();
                Console.WriteLine("Crap: an exception occurred:");
                Console.WriteLine(ex.ToString());
                return 2;
            }
        }

        private static int RunTests(Arguments args)
        {
            var packagename = args.Single("pn");
            var version = args.Single("v");
            var dontwait = args.Exists("dw");

            Console.WriteLine("Running tests for package: {0} v{1}", packagename, version);

            var options = new TestRunOptions
                              {
                                  TestAssemblies = args.SafeList("asm").ToArray(),
                                  TestClasses = args.SafeList("c").ToArray(),
                                  TestMethods = args.SafeList("m").ToArray(),
                                  DefaultEnvironment = args.Single("e"),
                                  RemoteControlHost = args.Single("h"),
                                  RemoteControlPort = Convert.ToInt32(args.Single("p"))
                              };
            if (options.TestAssemblies.Length == 1 && options.TestAssemblies[0].Contains(","))
                options.TestAssemblies = options.TestAssemblies[0].Split(',');

            if (options.RemoteControlHost != null)
                Console.WriteLine("Host: {0}:{1}", options.RemoteControlHost, options.RemoteControlPort);
                
            Console.WriteLine("Environment: {0}", options.DefaultEnvironment);

            Console.WriteLine("Assemblies:");


            Array.ForEach(options.TestAssemblies, Console.WriteLine);

            if (options.TestClasses.Length > 0)
            {
                Console.WriteLine("Test Classes:");
                Array.ForEach(options.TestClasses, Console.WriteLine);
            }

            if (options.TestMethods.Length > 0)
            {
                Console.WriteLine("Test Methods:");
                Array.ForEach(options.TestMethods, Console.WriteLine);
            }

            var client = CreateClient();
            var testrunid = client.RunTestPackageLibrary(packagename, version, options);

            if (dontwait)
            {
                Console.WriteLine("Test is running.  Test will continue to run in the background.");
                return 0;
            }

            Console.WriteLine("Test is running, waiting for test to complete.");
            for (;;)
            {
                Thread.Sleep(1000);
                if (!client.IsTestRunning(testrunid))
                    break;
            }

            string logtext;
            var result = client.TestRunResults(out logtext, testrunid);

            Console.WriteLine(logtext);
            Console.WriteLine(result);

            var parts = result.Split(',');
            var returncode = 0;

            foreach (var part in parts)
            {
                if (part.Contains("failed"))
                {
                    if (!part.StartsWith(" 0 "))
                    {
                        returncode = 3;
                        break;
                    }
                }
            }

            return returncode;
        }

        private static void UploadFiles(Arguments args)
        {
            var packagename = args.Single("pn");
            var version = args.Single("v");
            var extensions = args.SafeList("i");
            if (extensions.Count == 0)
            {
                extensions = args.SafeList("x"); // -x used for backwards compatibility
                if (extensions.Count == 0)
                    extensions.Add("*.*");
            }

            var paths = args.SafeList("path");
            if (paths.Count == 0)
                paths.Add(".");

            var files = new List<BinaryCompressedPackageFile>();

            foreach (var path in paths)
            {
                files.AddRange(BinaryCompressedPackageFile.GetAllFiles(path, extensions.ToArray()));
            }


            var client = CreateClient();
             
            Console.WriteLine("Creating package: {0} v{1}", packagename, version);

            var packageid = client.CreateBinaryPackage(packagename, version);

            Console.WriteLine("Uploading files to package {0}:", packageid);

            files.ForEach(bcpf =>
                              {
                                  Console.WriteLine("{0}", bcpf.Filename);
                                  if (client.CheckAddBinaryPackageFile(packageid, new BinaryFileInfo
                                                                                      {
                                                                                          FileHash = bcpf.Hash,
                                                                                          Filename = bcpf.Filename,
                                                                                          LastUpdateDate = DateTime.Now
                                                                                      }))
                                    client.AddBinaryPackageFile(packageid, bcpf);
                              });
            client.FinalizeBinaryPackage(packageid);

            Console.WriteLine("Complete.");
        }


        /// <summary>
        /// Creates a new instance of the QuailTestRunnerClient
        /// </summary>
        /// <returns>A new QuailTestRunnerClient</returns>
        private static QuailTestRunnerClient CreateClient()
        {
            var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
                              {
                                  ReaderQuotas =
                                      {
                                          MaxArrayLength = 20000000,
                                          MaxStringContentLength = 20000000
                                      },
                                  SendTimeout = new TimeSpan(0, 10, 0),
                                  ReceiveTimeout = new TimeSpan(0, 10, 0),
                                  MaxReceivedMessageSize = 2147483647,
                                  MaxBufferSize = 2147483647
                              };
            var endpoint = new EndpointAddress(QuailSettings.Default.QuailAddress);

            var result = new QuailTestRunnerClient(binding, endpoint);

            return result;
        }

        private static void ShowCommandLine()
        {
            Console.WriteLine("QuailDashboardClient");
            Console.WriteLine(@"
Command line arguments:
Major arguments:
  -u : Upload a new (or existing) file package
  -r : Schedule a test run from an existing file package
  @file : Arguments are in an external file

Argument descriptions:

Uploading files:
  -u : Upload a new (or existing) file package.  This allows you to upload a collection of files
       and later run the package as a test
    -pn [packagename] *
       Name of the package

    -v [versionnumber] *
       version name or number of the package

    -path [path] *
       Specify the path for the files to upload (may be specified more than once)

    -i [file wildcard]
       Specify the wildcards to apply.

    Example usage:
      -u -pn ""Test Package"" -v 1.4 -path c:\package\files -i *.dll -i *.pdb

Running a test:
  -r : Run a test from an existing file package
    -pn [packagename] *
       Name of the package

    -v [versionnumber] *
       version name or number of the package

    -e [environment] *
       The default environment to use

    -asm [Assembly file] *
       The name of the assembly to execute (may list more than one assembly)

    -c [Classname]
       The name of a test class to execute

    -m [Method name]
       The fully qualified name of a test method to execute

    -h [Host name]
       The name of the host to use

    -p [Port number]
       The port number to use

    -dw
       Set this option so it doesn't wait for the test run to complete

    Example usage:
      -r -e QA -pn ""Test Package"" -v 1.4 -asm test.dll
           executes all tests in test.dll

      -r -e QA -pn ""Test Package"" -v 1.4 -asm test.dll -c Test.Classname -c Test.OtherClassname -m Test.YetAnotherClass.Method1 -m Test.YetAnotherClass.Method2
           executs all non-explicit test methods in Test.Classname and Test.OtherClassname and test methods Test.YetAnotherClass' Method1 & Method2
  
");
        }
    }
}
